// SPDX-License-Identifier: GPL-2.0
/*
 * Loongson PCI Express Root-Complex driver
 *
 * Copyright (C) 2019 TangHaifeng <tang-haifeng@foxmail.com>
 *
 */

#include <common.h>
#include <pci.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <dm.h>
#include <linux/sizes.h>
#include <errno.h>

#if defined(CONFIG_MIPS)
#define PHYS_TO_UNCACHED
#elif defined(CONFIG_LOONGARCH)
#define CKSEG1ADDR PHYS_TO_UNCACHED
#endif

#define PCI_ACCESS_READ  0
#define PCI_ACCESS_WRITE 1

#define LS2X_IO_ADDR		0x18000000
#define LS2X_MEM_ADDR	0x10000000
#define LS2X_IO_SIZE		0x02000000
#define LS2X_MEM_SIZE	0x08000000

struct ls2x_pcie_priv {
	void __iomem *type0_base;
	void __iomem *type1_base;
};

void ls2x_pcie_dev_list(void)
{
	pci_dev_t busdevfunc;
	void *base;
	int i, num, device_id;

	device_id = PCI_DEVICE_ID_LOONGSON_GMAC;
	for (i=0; i<6; i++) {
		busdevfunc = pci_find_device(PCI_VENDOR_ID_LOONGSON, device_id + i, 0); /* get PCI Device ID */
		if (busdevfunc == -1) {
			dev_info(NULL, "pcie dev (%04X,%04X) not found\n", PCI_VENDOR_ID_LOONGSON, device_id + i);
		} else {
			base = pci_map_bar(busdevfunc, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
			dev_info(NULL, "pcie dev (%04X,%04X) %p\n", PCI_VENDOR_ID_LOONGSON, device_id + i, base);
		}
	}

	device_id = PCI_DEVICE_ID_LOONGSON_EHCI;
	busdevfunc = pci_find_device(PCI_VENDOR_ID_LOONGSON, device_id, 0); /* get PCI Device ID */
	if (busdevfunc == -1) {
		dev_info(NULL, "pcie dev (%04X,%04X) not found\n", PCI_VENDOR_ID_LOONGSON, device_id);
	} else {
		base = pci_map_bar(busdevfunc, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
		dev_info(NULL, "pcie dev (%04X,%04X) %p\n", PCI_VENDOR_ID_LOONGSON, device_id, base);
	}

	device_id = PCI_DEVICE_ID_LOONGSON_OHCI;
	busdevfunc = pci_find_device(PCI_VENDOR_ID_LOONGSON, device_id, 0); /* get PCI Device ID */
	if (busdevfunc == -1) {
		dev_info(NULL, "pcie dev (%04X,%04X) not found\n", PCI_VENDOR_ID_LOONGSON, device_id);
	} else {
		base = pci_map_bar(busdevfunc, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
		dev_info(NULL, "pcie dev (%04X,%04X) %p\n", PCI_VENDOR_ID_LOONGSON, device_id, base);
	}

#ifdef CONFIG_SB_LS7A1000
	device_id = PCI_DEVICE_ID_LOONGSON_GPU;
	busdevfunc = pci_find_device(PCI_VENDOR_ID_LOONGSON, device_id, 0); /* get PCI Device ID */
	if (busdevfunc == -1) {
		dev_info(NULL, "pcie dev (%04X,%04X) not found\n", PCI_VENDOR_ID_LOONGSON, device_id);
	} else {
		base = pci_map_bar(busdevfunc, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
		dev_info(NULL, "pcie dev (%04X,%04X) %p\n", PCI_VENDOR_ID_LOONGSON, device_id, base);

		base = pci_map_bar(busdevfunc, PCI_BASE_ADDRESS_2, PCI_REGION_MEM);
		dev_info(NULL, "pcie dev (%04X,%04X) %p\n", PCI_VENDOR_ID_LOONGSON, device_id, base);
	}
#endif

	device_id = PCI_DEVICE_ID_LOONGSON_PCIE0;
	num = 4;
	for (i=0; i<num; i++) {
		busdevfunc = pci_find_device(PCI_VENDOR_ID_LOONGSON, device_id, i); /* get PCI Device ID */
		if (busdevfunc == -1) {
			dev_info(NULL, "pcie dev (%04X,%04X) not found\n", PCI_VENDOR_ID_LOONGSON, device_id);
		} else {
			base = pci_map_bar(busdevfunc, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
			dev_info(NULL, "pcie dev (%04X,%04X) %p\n", PCI_VENDOR_ID_LOONGSON, device_id, base);
		}
	}

	device_id = PCI_DEVICE_ID_LOONGSON_PCIE1;
#ifdef CONFIG_SB_LS7A1000
	num = 5;
#else
	num = 2;
#endif
	for (i=0; i<num; i++) {
		busdevfunc = pci_find_device(PCI_VENDOR_ID_LOONGSON, device_id, i); /* get PCI Device ID */
		if (busdevfunc == -1) {
			dev_info(NULL, "pcie dev (%04X,%04X) not found\n", PCI_VENDOR_ID_LOONGSON, device_id);
		} else {
			base = pci_map_bar(busdevfunc, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
			dev_info(NULL, "pcie dev (%04X,%04X) %p\n", PCI_VENDOR_ID_LOONGSON, device_id, base);
		}
	}

#ifdef CONFIG_SB_LS7A1000
	device_id = PCI_DEVICE_ID_LOONGSON_PCIE2;
	num = 3;
	for (i=0; i<num; i++) {
		busdevfunc = pci_find_device(PCI_VENDOR_ID_LOONGSON, device_id, i); /* get PCI Device ID */
		if (busdevfunc == -1) {
			dev_info(NULL, "pcie dev (%04X,%04X) not found\n", PCI_VENDOR_ID_LOONGSON, device_id);
		} else {
			base = pci_map_bar(busdevfunc, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
			dev_info(NULL, "pcie dev (%04X,%04X) %p\n", PCI_VENDOR_ID_LOONGSON, device_id, base);
		}
	}

	device_id = PCI_DEVICE_ID_LOONGSON_SPI;
	busdevfunc = pci_find_device(PCI_VENDOR_ID_LOONGSON, device_id, 0);
	if (busdevfunc == -1) {
		dev_info(NULL, "pcie dev (%04X,%04X) not found\n", PCI_VENDOR_ID_LOONGSON, device_id);
	} else {
		base = pci_map_bar(busdevfunc, PCI_BASE_ADDRESS_0, PCI_REGION_MEM);
		dev_info(NULL, "pcie dev (%04X,%04X) %p\n", PCI_VENDOR_ID_LOONGSON, device_id, base);
	}
#endif
}

int ls2x_pci_config_access(struct ls2x_pcie_priv *priv, u8 access_type,
   pci_dev_t tag, int where, u32 *data)
{
	void __iomem *addrp;
	int busnum, device, function;
	int reg = where & ~3;

	busnum = PCI_BUS(tag);
	device = PCI_DEV(tag);
	function = PCI_FUNC(tag);

	if (busnum == 0) {
		/* in-chip virtual-bus has no parent,
		    so access is routed to PORT_HEAD */
		if (device > 31 || device == 2) {
			/* only one Controller lay on a virtual-bus */
			return -EINVAL;
		}

		addrp = (void __iomem *)((ulong)priv->type0_base |
				  (((device << 11) | (function << 8) | reg) & 0xffff));
	} else {
		//pcie设备扫描问题：对于pcie桥，扫描下级总线时，当扫描非0号设备时，本应返回无效值，但本桥会返回0号设备的配置头，
		//造成0号设备被重复发现。
		//这里大于0号设备返回无效值，不再扫描
		if (device > 0) {
			return -EINVAL;
		}

		addrp = (void __iomem *)((ulong)priv->type1_base |
				  ((busnum << 16) | (device << 11) | (function << 8) | reg));
	}

	if (access_type == PCI_ACCESS_WRITE) {
		writel(cpu_to_le32(*data), addrp);
	} else {
		*data = le32_to_cpu(readl(addrp));

		if (busnum == 0 && reg == PCI_CLASS_REVISION && (*data>>16) == PCI_CLASS_PROCESSOR_MIPS)
			*data = (PCI_CLASS_BRIDGE_PCI<<16) | (*data & 0xffff);
		if (busnum == 0 && reg == 0x3c &&  (*data &0xff00) == 0)
			*data |= 0x100;
	}

	return 0;
}

#if defined(CONFIG_CPU_LOONGSON2K1000)
#include <mach/ls2k.h>
static void ls2x_pcie_setup(void)
{
	u32 val = 0;

	//pcie phy 配置
	writel(0xc2492331, (unsigned int *)CKSEG1ADDR(LS2X_PCIE0_CONF0));
	writel(0xff3ff0a8, (unsigned int *)CKSEG1ADDR(LS2X_PCIE0_CONF0 + 4));
	writel(0xc2492331, (unsigned int *)CKSEG1ADDR(LS2X_PCIE1_CONF0));
	writel(0xff3ff0a8, (unsigned int *)CKSEG1ADDR(LS2X_PCIE1_CONF0 + 4));

	writel(0x00027fff, (unsigned int *)CKSEG1ADDR(LS2X_PCIE0_CONF1));
	writel(0x00027fff, (unsigned int *)CKSEG1ADDR(LS2X_PCIE1_CONF1));
#if 0
	writel(0x00000001, (unsigned int *)CKSEG1ADDR(LS2X_PCIE0_PHY + 4));
	writel(0x4fff1002, (unsigned int *)CKSEG1ADDR(LS2X_PCIE0_PHY));
	writel(0x00000001, (unsigned int *)CKSEG1ADDR(LS2X_PCIE1_PHY + 4));
	writel(0x4fff1002, (unsigned int *)CKSEG1ADDR(LS2X_PCIE1_PHY));
	mdelay(5);
	writel(0x00000001, (unsigned int *)CKSEG1ADDR(LS2X_PCIE0_PHY + 4));
	writel(0x4fff1102, (unsigned int *)CKSEG1ADDR(LS2X_PCIE0_PHY));
	writel(0x00000001, (unsigned int *)CKSEG1ADDR(LS2X_PCIE1_PHY + 4));
	writel(0x4fff1102, (unsigned int *)CKSEG1ADDR(LS2X_PCIE1_PHY));
	mdelay(5);
	writel(0x00000001, (unsigned int *)CKSEG1ADDR(LS2X_PCIE0_PHY + 4));
	writel(0x4fff1202, (unsigned int *)CKSEG1ADDR(LS2X_PCIE0_PHY));
	writel(0x00000001, (unsigned int *)CKSEG1ADDR(LS2X_PCIE1_PHY + 4));
	writel(0x4fff1202, (unsigned int *)CKSEG1ADDR(LS2X_PCIE1_PHY));
	mdelay(5);
	writel(0x00000001, (unsigned int *)CKSEG1ADDR(LS2X_PCIE0_PHY + 4));
	writel(0x4fff1302, (unsigned int *)CKSEG1ADDR(LS2X_PCIE0_PHY));
	writel(0x00000001, (unsigned int *)CKSEG1ADDR(LS2X_PCIE1_PHY + 4));
	writel(0x4fff1302, (unsigned int *)CKSEG1ADDR(LS2X_PCIE1_PHY));
	mdelay(5);
#else
	writel(0x4fff1002, (unsigned int *)CKSEG1ADDR(LS2X_PCIE0_PHY));
	writel(0x00000001, (unsigned int *)CKSEG1ADDR(LS2X_PCIE0_PHY + 4));
	writel(0x4fff1002, (unsigned int *)CKSEG1ADDR(LS2X_PCIE1_PHY));
	writel(0x00000001, (unsigned int *)CKSEG1ADDR(LS2X_PCIE1_PHY + 4));
	mdelay(5);
	writel(0x4fff1102, (unsigned int *)CKSEG1ADDR(LS2X_PCIE0_PHY));
	writel(0x00000001, (unsigned int *)CKSEG1ADDR(LS2X_PCIE0_PHY + 4));
	writel(0x4fff1102, (unsigned int *)CKSEG1ADDR(LS2X_PCIE1_PHY));
	writel(0x00000001, (unsigned int *)CKSEG1ADDR(LS2X_PCIE1_PHY + 4));
	mdelay(5);
	writel(0x4fff1202, (unsigned int *)CKSEG1ADDR(LS2X_PCIE0_PHY));
	writel(0x00000001, (unsigned int *)CKSEG1ADDR(LS2X_PCIE0_PHY + 4));
	writel(0x4fff1202, (unsigned int *)CKSEG1ADDR(LS2X_PCIE1_PHY));
	writel(0x00000001, (unsigned int *)CKSEG1ADDR(LS2X_PCIE1_PHY + 4));
	mdelay(5);
	writel(0x4fff1302, (unsigned int *)CKSEG1ADDR(LS2X_PCIE0_PHY));
	writel(0x00000001, (unsigned int *)CKSEG1ADDR(LS2X_PCIE0_PHY + 4));
	writel(0x4fff1302, (unsigned int *)CKSEG1ADDR(LS2X_PCIE1_PHY));
	writel(0x00000001, (unsigned int *)CKSEG1ADDR(LS2X_PCIE1_PHY + 4));
	mdelay(5);
#endif
	//使能pcie
	val = readl((unsigned int *)CKSEG1ADDR(LS2X_COMMON_CONF2));
	val = val | (1<<16) | (1<<17);
	writel(val, (unsigned int *)CKSEG1ADDR(LS2X_COMMON_CONF2));
	mdelay(5);
	//配置头设置
	//pcie0 port0
	writel(0x11000000, (unsigned int *)PHYS_TO_UNCACHED(LS2X_TYPE0_ADDR | 0x4800 | 0x10));
	writel(0x00ff204c, (unsigned int *)PHYS_TO_UNCACHED(0x11000000));
	//pcie0 port1
	writel(0x11100000, (unsigned int *)PHYS_TO_UNCACHED(LS2X_TYPE0_ADDR | 0x5000 | 0x10));
	writel(0x00ff204c, (unsigned int *)PHYS_TO_UNCACHED(0x11100000));
	//pcie0 port2
	writel(0x11200000, (unsigned int *)PHYS_TO_UNCACHED(LS2X_TYPE0_ADDR | 0x5800 | 0x10));
	writel(0x00ff204c, (unsigned int *)PHYS_TO_UNCACHED(0x11200000));
	//pcie0 port3
	writel(0x11300000, (unsigned int *)PHYS_TO_UNCACHED(LS2X_TYPE0_ADDR | 0x6000 | 0x10));
	writel(0x00ff204c, (unsigned int *)PHYS_TO_UNCACHED(0x11300000));

	//pcie1 port0
	writel(0x10000000, (unsigned int *)PHYS_TO_UNCACHED(LS2X_TYPE0_ADDR | 0x6800 | 0x10));
#if defined(CONFIG_TARGET_LS2K1000_PC_EVB)
	writel(0x00ff204f, (unsigned int *)PHYS_TO_UNCACHED(0x10000000));
#else
	writel(0x00ff204c, (unsigned int *)PHYS_TO_UNCACHED(0x10000000));
#endif
	//pcie1 port1
	writel(0x10100000, (unsigned int *)PHYS_TO_UNCACHED(LS2X_TYPE0_ADDR | 0x7000 | 0x10));
	writel(0x00ff204c, (unsigned int *)PHYS_TO_UNCACHED(0x10100000));

	mdelay(50);//等待pcie reset信号完成
}
#elif defined(CONFIG_CPU_LOONGSON2K500)
#ifdef CONFIG_MIPS
u32 __raw__readw(u64 addr);
u32 __raw__writew(u64 addr, u32 val);
#endif

static void ls2x_pcie_phy_write(u32 cfg_val)
{
	void __iomem *reg;
#ifdef CONFIG_MIPS
	reg = ioremap(0x1fe10540, 1);
	writel(cfg_val, reg);
	writel(0x1, reg + 0x4);

	writel(cfg_val, reg + 0x20);
	writel(0x1, reg + 0x20 + 0x4);
#else
	u32 val = 0;
	reg = ioremap(0x1fe10560, 1);
	writel(cfg_val, reg);
	writel(0x1, reg + 0x4);
	do {
		val = readl(reg + 0x4);
	} while ((val & 0x4) == 0);
#endif
}

static void ls2x_pcie_port_conf(int port)
{
	u32 val = 0;
	void __iomem *reg;

#ifdef CONFIG_MIPS
	u64 addr = 0x900000fe0800000cULL | ((u64)port << 11);
	val = __raw__readw(addr);
	val = val & 0xfff9ffff;
	val = val | 0x00020000;
	__raw__writew(addr, val);
#else
	reg = ioremap(0xfe0800000c | (port << 11), 1);
	val = readl(reg);
	val = val & 0xfff9ffff;
	val = val | 0x00020000;
	writel(val, reg);
#endif

#ifdef CONFIG_MIPS
	addr = 0x900000fe0700001cULL | ((u64)port << 11);
	val = __raw__readw(addr);
	val = val | (0x1 << 26);
	__raw__writew(addr, val);
#else
	reg = ioremap(0xfe0700001c | (port << 11), 1);
	val = readl(reg);
	val = val | (0x1 << 26);
	writel(val, reg);
#endif

#ifdef CONFIG_MIPS
	reg = ioremap(0x16000000 | (port << 12), 1);
#else
	reg = ioremap(0x16000000 | (port << 1), 1);
#endif
	val = readl(reg + 0x54);
	val = val & (~((0x7 << 18) | (0x7 << 2)));
	writel(val, reg + 0x54);
	val = readl(reg + 0x58);
	val = val & (~((0x7 << 18) | (0x7 << 2)));
	writel(val, reg + 0x58);

	writel(0x00ff204f, reg);
}

static void ls2x_pcie_setup(void)
{
	u32 val = 0;
	void __iomem *reg;

	ls2x_pcie_phy_write(0x4fff1002);
	ls2x_pcie_phy_write(0x4fff1102);
	ls2x_pcie_phy_write(0x4fff1202);
	ls2x_pcie_phy_write(0x4fff1302);

	ls2x_pcie_port_conf(0);
	ls2x_pcie_port_conf(1);

	reg = ioremap(0x1fe10120, 1);
	val = readl(reg);
	if ((val & (0x1<6)) && (val & (0x1<11))) {
		/*set ep device 0,1 class type to 0x0600*/
		void __iomem *port_ctrl = ioremap(0x16000000, 1);
		void __iomem *port_cfg = ioremap(0x16800000, 1);
		writel(0x21ff204f, port_ctrl);
		writel(0x06000001, port_cfg + 0x8);
		writel(0x06000001, port_cfg + 0x1000 + 0x8);
		writel(0x01ff204f, port_ctrl);
		/*set ep device 0,1 bar size to 256M*/
		writel(0x0fffffff, port_cfg + 0x110);
		writel(0x0fffffff, port_cfg + 0x1000 + 0x110);
		writel(0x000f0000, port_ctrl + 0x68);
		writel(0x00000000, port_ctrl + 0x70);
	} else {
		/*set pcie controler device 0 bar size to 4K*/
//		void __iomem *port_cfg = ioremap(0xfe00000000, 1);
		void __iomem *port_cfg = ioremap(0x16800000, 1);
		writel(0x00000fff, port_cfg + 0x110);
		writel(0x00000fff, port_cfg + 0x800 + 0x110);
	}
}
#endif

#if !CONFIG_IS_ENABLED(DM_PCI)
static struct ls2x_pcie_priv ls2x_pcie_priv = {
	.type0_base	= (void __iomem *)LS2X_TYPE0_ADDR,
	.type1_base	= (void __iomem *)LS2X_TYPE1_ADDR,
};

static struct ls2x_pcie_priv *priv = &ls2x_pcie_priv;

static int ls2x_pcie_read_config(struct pci_controller *hose, pci_dev_t d,
				int where, u32 *val)
{
	struct ls2x_pcie_priv *priv = hose->priv_data;
	u32 data = 0;
	int ret = 0;

	ret = ls2x_pci_config_access(priv, PCI_ACCESS_READ, d, where, &data);
	if (ret) {
		*val = 0xffffffff;
		return 0;
	}

	*val = data;

	return 0;
}

static int ls2x_pcie_write_config(struct pci_controller *hose, pci_dev_t d,
				 int where, u32 val)
{
	struct ls2x_pcie_priv *priv = hose->priv_data;
	u32 data = val;

	return ls2x_pci_config_access(priv, PCI_ACCESS_WRITE, d, where, &data);
}

void pci_init_board(void)
{
	/* Static instance of the controller. */
	static struct pci_controller	pcc;
	struct pci_controller		*hose = &pcc;

	memset(&pcc, 0, sizeof(pcc));

	hose->priv_data = priv;

#if defined(CONFIG_CPU_LOONGSON2K1000) || defined(CONFIG_CPU_LOONGSON2K500)
	ls2x_pcie_setup();
#endif

	/* PCI I/O space */
	pci_set_region(&hose->regions[0],
			LS2X_IO_ADDR, LS2X_IO_ADDR,
			LS2X_IO_SIZE, PCI_REGION_IO);

	/* PCI memory space */
	pci_set_region(&hose->regions[1],
			LS2X_MEM_ADDR, LS2X_MEM_ADDR,
			LS2X_MEM_SIZE, PCI_REGION_MEM);

	/*
	* System memory space
	* This is also a hacking, although I don't like it
	* In fact the phys is virt address
	*/
	pci_set_region(&hose->regions[2],
		0x00000000, 0x00000000,
		0x80000000, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);

	hose->region_count = 3;

	pci_set_ops(hose,
		    pci_hose_read_config_byte_via_dword,
		    pci_hose_read_config_word_via_dword,
		    ls2x_pcie_read_config,
		    pci_hose_write_config_byte_via_dword,
		    pci_hose_write_config_word_via_dword,
		    ls2x_pcie_write_config);

	pci_register_hose(hose);
	hose->last_busno = pci_hose_scan(hose);
	debug("hose->last_busno = %d\n", hose->last_busno);
}
#else
static int ls2x_pcie_dm_read_config(struct udevice *dev, pci_dev_t bdf,
				   uint offset, ulong *value,
				   enum pci_size_t size)
{
	struct ls2x_pcie_priv *priv = dev_get_priv(dev);
	u32 tmpval;
	int ret;

	ret = ls2x_pci_config_access(priv, PCI_ACCESS_READ, bdf, offset, &tmpval);
	if (ret) {
		tmpval = 0xffffffff;
	}

	*value = pci_conv_32_to_size(tmpval, offset, size);
	return 0;
}

static int ls2x_pcie_dm_write_config(struct udevice *dev, pci_dev_t bdf,
				    uint offset, ulong value,
				    enum pci_size_t size)
{
	struct ls2x_pcie_priv *priv = dev_get_priv(dev);
	u32 tmpval, newval;
	int ret;

	ret = ls2x_pci_config_access(priv, PCI_ACCESS_READ, bdf, offset, &tmpval);
	if (ret)
		return ret;

	newval = pci_conv_size_to_32(tmpval, value, offset, size);
	return ls2x_pci_config_access(priv, PCI_ACCESS_WRITE, bdf, offset, &newval);
}

static int ls2x_pcie_dm_probe(struct udevice *dev)
{
#if defined(CONFIG_CPU_LOONGSON2K1000) || defined(CONFIG_CPU_LOONGSON2K500)
	ls2x_pcie_setup();
#endif
	return 0;
}

static int ls2x_pcie_dm_remove(struct udevice *dev)
{
	return 0;
}

static int ls2x_pcie_ofdata_to_platdata(struct udevice *dev)
{
	struct ls2x_pcie_priv *priv = dev_get_priv(dev);

	//2k500 pcie rc模式时只做桥设备(type1)，type0没用，这里地址写成一样
	priv->type0_base = (void __iomem *)map_physmem(devfdt_get_addr_index(dev, 0), 0, MAP_NOCACHE);
	priv->type1_base = (void __iomem *)map_physmem(devfdt_get_addr_index(dev, 1), 0, MAP_NOCACHE);
	if (!priv->type0_base || !priv->type1_base)
		return -EINVAL;

	return 0;
}

static const struct dm_pci_ops ls2x_pcie_ops = {
	.read_config	= ls2x_pcie_dm_read_config,
	.write_config	= ls2x_pcie_dm_write_config,
};

static const struct udevice_id ls2x_pcie_ids[] = {
	{ .compatible = "loongson,ls2x-pcie" },
	{ }
};

U_BOOT_DRIVER(ls2x_pcie) = {
	.name			= "ls2x_pcie",
	.id			= UCLASS_PCI,
	.of_match		= ls2x_pcie_ids,
	.ops			= &ls2x_pcie_ops,
	.probe			= ls2x_pcie_dm_probe,
	.remove			= ls2x_pcie_dm_remove,
	.ofdata_to_platdata	= ls2x_pcie_ofdata_to_platdata,
	.priv_auto_alloc_size	= sizeof(struct ls2x_pcie_priv),
	.flags			= DM_FLAG_OS_PREPARE,
};
#endif
